using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using ActivateYourGlutes.Infrastructure;
using ActivateYourGlutes.Model;
using ActivateYourGlutes.Model.Contracts;
using ActivateYourGlutes.Model.Providers;
using ActivateYourGlutes.Helpers;
using ActivateYourGlutes.Payloads.Body;
using ActivateYourGlutes.Payloads.Shared;

namespace ActivateYourGlutes.Controllers
{
    public partial class BodyController : LocalisingController
    {
        private readonly IBodyProvider _bodyProvider;
        private readonly IMeasurementTypeProvider _measurementTypeProvider;

        public BodyController(IBodyProvider bodyProvider, IMeasurementTypeProvider measurementTypeProvider)
        {
            _bodyProvider = bodyProvider;
            _measurementTypeProvider = measurementTypeProvider;
        }

        [Authorize(Roles="User")]
        public virtual ActionResult Index()
        {
            var horizontalMeasurementTypesPayload = new HorizontalMeasurementTypesPayload
                                                        {
                                                            SelectedTypes = _bodyProvider.DefaultMeasurementTypes,
                                                            IncludeButton = true,
                                                            MeasurementTypes = _measurementTypeProvider.MeasurementTypes(_bodyProvider.CurrentAccount(User)),
                                                        };
            string primaryTitle;
            string secondaryTitle;
            var series = Series(
                horizontalMeasurementTypesPayload.SelectedTypes,
                out primaryTitle,
                out secondaryTitle);

            ViewData.Model = new IndexPayload
                                 {
                                     HorizontalMeasurementTypesPayload = horizontalMeasurementTypesPayload,
                                     PrimarySeriesTitle = primaryTitle,
                                     SecondarySeriesTitle = secondaryTitle,
                                     Series = series
                                 };
            return View();
        }

        [Authorize(Roles = "User")]
        [AcceptVerbs(HttpVerbs.Post)]
        [ValidateAntiForgeryToken]
        public virtual ActionResult Index(FormCollection formCollection)
        {
            var measurementTypes = _measurementTypeProvider.MeasurementTypes(_bodyProvider.CurrentAccount(User));
            var selectedTypes = new List<MeasurementType>();
            foreach (var type in measurementTypes)
            {
                var keyInForm = formCollection[type.ToFormName()];
                var isChecked = keyInForm != "false" && !String.IsNullOrEmpty(keyInForm);
                if (isChecked)
                    selectedTypes.Add(type);
            }

            var horizontalMeasurementTypesPayload = new HorizontalMeasurementTypesPayload
                                                        {
                SelectedTypes = selectedTypes,
                IncludeButton = true,
                MeasurementTypes = measurementTypes,
            };
            
            string primaryTitle;
            string secondaryTitle;
            var series = Series(selectedTypes, out primaryTitle, out secondaryTitle);
            
            ViewData.Model = new IndexPayload
                                 {
                                     HorizontalMeasurementTypesPayload = horizontalMeasurementTypesPayload,
                                     PrimarySeriesTitle = primaryTitle,
                                     SecondarySeriesTitle = secondaryTitle,
                                     Series = series
                                 };

            return View();
        }

        private List<string> Series(IEnumerable<MeasurementType> types, out string primaryTitle, out string secondaryTitle)
        {
            var results = new List<string>();
            var account = _bodyProvider.CurrentAccount(User);
            var first = true;
            primaryTitle = null;
            secondaryTitle = null;
            
            foreach(var type in types)
            {
                var indicatorsSeries = _bodyProvider.SeriesForIndicator(account, type, false);
                var series = new StringBuilder();
                series.Append("<vc:DataSeries LegendText=\"");
                series.Append(indicatorsSeries.Title);
                if (first)
                {
                    primaryTitle = indicatorsSeries.Title;
                    series.Append("\" RenderAs=\"Line\" AxisYType=\"Primary\" XValueType=\"DateTime\" XValueFormatString=\"d MMM yyyy\" >");
                    first = false;
                }
                else
                {
                    secondaryTitle = indicatorsSeries.Title;
                    series.Append("\" RenderAs=\"Line\" AxisYType=\"Secondary\" XValueType=\"DateTime\" XValueFormatString=\"d MMM yyyy\" >");
                }
                series.Append("<vc:DataSeries.DataPoints>");

                foreach (var seriesValuePair in indicatorsSeries.Values)
                {
                    if (seriesValuePair.Y == null) continue;
                    series.Append("<vc:DataPoint ");
                    series.Append(" XValue=\"");
                    series.Append(seriesValuePair.XDate.ToString("MM/dd/yyyy"));
                    series.Append("\" YValue=\"");
                    series.Append(seriesValuePair.Y);
                    series.Append("\" />");
                }

                series.Append("</vc:DataSeries.DataPoints>");
                series.Append("</vc:DataSeries>");
                results.Add(series.ToString());
            }

            if (results.Count > 2)
                secondaryTitle = "Others";

            return results;
        }
        

        [Authorize(Roles = "User")]
        public virtual ActionResult Grid()
        {
            var measurementTypes = _measurementTypeProvider.MeasurementTypes(_bodyProvider.CurrentAccount(User));
            var columnHeaderBuilder = new StringBuilder("'Actions', 'Date' ");
            var columnModelBuilder =
                new StringBuilder(
                    "{ name: 'Actions', index:'Actions', width:75, sortable:false }, { name: 'Date', index: 'Date', align: 'left' }");
            foreach(var type in measurementTypes)
            {
                columnHeaderBuilder.Append(", '");
                columnHeaderBuilder.Append(type.Description);
                columnHeaderBuilder.Append("'");

                var name = String.Format("m{0}", type.MeasurementTypeId);
                columnModelBuilder.Append(", { name: '");
                columnModelBuilder.Append(name);
                columnModelBuilder.Append("', index: '");
                columnModelBuilder.Append(name);
                columnModelBuilder.Append("', align: 'right' }");
            }

            ViewData.Model = new GridPayload
                                 {
                                     ColumnHeaders = columnHeaderBuilder.ToString(),
                                     ColumnModel = columnModelBuilder.ToString()
                                 };

            return View();
        }

        [Authorize(Roles = "User")]
        public virtual ActionResult GridData(string sidx, string sord, int page, int rows)
        {
            var pageIndex = Convert.ToInt32(page) - 1;
            var pageSize = rows;
            var account = _bodyProvider.CurrentAccount(User);
            var totalRecords = _bodyProvider.TotalBodiesForAccount(account);
            var totalPages = (int)Math.Ceiling(totalRecords / (float)pageSize);
            var bodies = _bodyProvider.BodiesForAccount(account, pageIndex*pageSize, pageSize);
            var measurementTypes = _measurementTypeProvider.MeasurementTypes(account);

            var jsonData = new
            {
                total = totalPages,
                page,
                records = totalRecords,
                rows = (
                    from body in bodies.ToArray()
                    select new
                    {
                        id = body.BodyId,
                        cell =
                            (from s in new [] { "", body.EntryDate.ToString(LocaleSettings.DateFormat) } select s).
                            Concat(
                                (from type in measurementTypes
                                join measurement in body.Measurements
                                on type.MeasurementTypeId equals measurement.MeasurementType.MeasurementTypeId
                                into joinedMeasurements
                                from joinedMeasurement in joinedMeasurements.DefaultIfEmpty()
                                select joinedMeasurement == null ? "" : joinedMeasurement.LocalisedValue.ToString())
                            ).ToArray()
                    }
                ).ToArray()
            };


            return Json(jsonData);
        }

        [Authorize(Roles = "User")]
        public virtual ActionResult DeleteBody(int? id)
        {
            if (Request.IsAjaxRequest() && id != null)
            {
                try
                {
                    var account = _bodyProvider.CurrentAccount(User);
                    _bodyProvider.Delete(account, id.Value);                        
                }
                catch (Exception)
                {
                    // log error
                    return Json(false);
                }
                return Json(true);
            }
            return new EmptyResult();
        }

        [Authorize(Roles = "User")]
        public virtual ActionResult Create()
        {
            SetCreateViewData(new Body());
            return View();
        }

        private void SetCreateViewData(Body body)
        {
            var account = _bodyProvider.CurrentAccount(User);
            ViewData.Model = new BodyEditorPayload(
                LocaleSettings,
                BodyEditorPayload.EditModeEnum.Create,
                _measurementTypeProvider.MeasurementTypes(account),
                body,
                _bodyProvider.ConsolidatedBody(account));
        }

        [Authorize(Roles = "User")]
        [AcceptVerbs(HttpVerbs.Post)]
        [ValidateAntiForgeryToken]
        public virtual ActionResult Create([Bind(Exclude = "BodyId")] Body body)
        {
            if (!ModelState.IsValid)
            {
                SetCreateViewData(body);
                return View();
            }

            try
            {
                DateTime today = DateTime.Now;
                body.Account = _bodyProvider.CurrentAccount(User);
                body.EntryDate = new DateTime(body.EntryDate.Year, body.EntryDate.Month, body.EntryDate.Day, today.Hour, today.Minute, today.Second, today.Millisecond);
                _bodyProvider.Add(body);

                return RedirectToAction("Grid");
            }
            catch(ValidationException ex)
            {
                SetCreateViewData(body);
                ex.AddToModelState(ViewData.ModelState);
                return View();
            }
            catch
            {
                SetCreateViewData(body);
                ViewData.ModelState.AddModelError("_FORM", "Unexpected error occurred while saving.");
                return View();
            }
        }

        private void SetEditViewData(Body body)
        {
            ViewData.Model = new BodyEditorPayload(LocaleSettings,
                                                   BodyEditorPayload.EditModeEnum.Edit,
                                                   _measurementTypeProvider.MeasurementTypes(_bodyProvider.CurrentAccount(User)),
                                                   body);
        }

        [Authorize(Roles = "User")]
        public virtual ActionResult Edit(int id)
        {
            var account = _bodyProvider.CurrentAccount(User);
            try
            {
                var body = _bodyProvider.GetBodyById(account, id);
                SetEditViewData(body);
                return View();
            }
            catch (AuthorizationException)
            {
                return RedirectToAction("Unauthorized", "Account");
            }            
        }

        [Authorize(Roles = "User")]
        [AcceptVerbs(HttpVerbs.Post)]
        [ValidateAntiForgeryToken]
        public virtual ActionResult Edit(int id, [Bind(Exclude = "BodyId")] Body body)
        {
            var account = _bodyProvider.CurrentAccount(User);
            SetEditViewData(body);
            try
            {
                body.BodyId = id;
                
                if (ModelState.IsValid)
                {
                    _bodyProvider.Update(account, body);
                    return RedirectToAction("Grid");
                }
            }
            catch (AuthorizationException)
            {
                return RedirectToAction("Unauthorized", "Account");
            }
            catch (ValidationException ex)
            {
                ex.AddToModelState(ViewData.ModelState);
            }
            return View();
        }

        [Authorize(Roles="User")]
        public void ExportToXml()
        {
            Response.ContentType = "text/xml";
            Response.Clear();
            Response.BufferOutput = true;
            var account = _bodyProvider.CurrentAccount(User);
            _bodyProvider.ToXml(account, Response.OutputStream);
            Response.Flush();
        }
    }
}
